#include <unistd.h>
#include <string.h>
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include "ktota.h"
#include "ktota_master.h"
#include "simple_uart.h"
#include "crc.h"
#include "easyjs.h"

int is_run = 1;

#define MSG0_HEAD0 0xaa
#define MSG0_HEAD1 0x55
#define MSG0_HEAD2 0x61

#define CMD_HELP "help"
#define CMD_START "start"
#define CMD_STOP "stop"
#define CMD_CLOSE "close"
#define msg0_dbg(...) 

#define MSG0_MINI_LEN (5 + 2)

#define MSGTYPE_OTA         4   /* ota升级 */


/* 实现接口 */
uint32_t ota_calcrc32 (const uint8_t *data, uint32_t size) {
    return calcrc32(data, size);
}

static void low_print_array(uint8_t *buf, uint16_t len) 
{
    uint16_t i;
    for(i = 0; i < len; i++) {
        printf("%02x ", buf[i]);
    }
    printf("\r\n");
}


static uint16_t _check_sum(uint8_t *buf, uint16_t len) {
    uint16_t sum = 0, i = 0;
    
    for(i = 0; i < len; i++) {
        sum+= buf[i];
    }
    printf("sum is %x %x %x %d\r\n", buf[0], buf[i], sum, len);
    return sum;
}

static void _msg0_parse_frame(uint8_t *buf, uint16_t buflen) {
    uint8_t type = buf[3];
    switch(type) {
    case MSGTYPE_OTA:
        ktota_master_parse_ack(buf+5, 4);
        break;
    }
}

static void _msg0_parse(uint8_t *buf, uint16_t buflen) {
    uint8_t *data = buf, *farme;
    uint16_t framelen, csum, ssum;
    while(buflen >= MSG0_MINI_LEN) {
        if( (data[0] == MSG0_HEAD0) && (data[1] == MSG0_HEAD1) && (data[2] == MSG0_HEAD2) ) {
            framelen = data[4];
            if(framelen > buflen || framelen < MSG0_MINI_LEN) {
                msg0_dbg("err: frame len %d\r\n", framelen);
                buflen-= 3;
                data+= 3;
                continue;
            }
            farme = data;
            buflen-= framelen;
            data+= framelen;
            csum = _check_sum(farme, framelen - 2);
            ssum = farme[framelen-1] | ((uint16_t)farme[framelen-2] << 8);
            if(csum == ssum) {
                _msg0_parse_frame(farme, framelen);
            }else {
                msg0_dbg("err: check sum do't match %x / %x \r\n", csum, ssum);
            }
        }else{
            buflen--;
            data++;
        }
    }
}

void msg_send(uint8_t *buf, uint32_t len) {
    static uint8_t msg_buf[KTOTA_MAX_PACK_LEN + 128];
    uint32_t framlen = 0;
    uint32_t idx = 0;
    uint16_t sum = 0;
    if(len > KTOTA_MAX_PACK_LEN) {
        printf("msg too long\r\n");
        return;
    }
    /* pack head */
    msg_buf[idx++] = MSG0_HEAD0;
    msg_buf[idx++] = MSG0_HEAD1;
    if(len > 240) {
        framlen = len + 8;
        msg_buf[idx++] = MSG0_HEAD2 + 1;
        msg_buf[idx++] = MSGTYPE_OTA;
        msg_buf[idx++] = (uint8_t)(framlen);
        msg_buf[idx++] = (uint8_t)(framlen >> 8);
    }else {
        framlen = len + 7;
        msg_buf[idx++] = MSG0_HEAD2;
        msg_buf[idx++] = MSGTYPE_OTA;
        msg_buf[idx++] = (uint8_t)(framlen);
    }
    memcpy(msg_buf + idx, buf, len);
    idx+= len;
    sum = _check_sum(msg_buf, (uint16_t)idx);
    msg_buf[idx++] = (uint8_t)(sum >> 8);
    msg_buf[idx++] = (uint8_t)sum;
    simple_uart_send(msg_buf, idx);
    printf("send %d\r\n", idx);
    low_print_array(msg_buf, idx);
}

void demo_cmd_init(void) {
#define STDIN 0
    long flags = fcntl(STDIN, F_GETFL, 0);
    flags |= O_NONBLOCK;
    fcntl(STDIN, F_SETFL, flags);
}

void demo_cmd_loop(void) {
    int ret  = 0;
    char buf[64];
    ret = scanf("%s", buf);
    if(ret <= 0) {
        return;
    }
    if(0 == strcmp(buf, CMD_HELP)) {
        printf("cmd str: start | stop | close\n"); 
    }else if(0 == strcmp(buf, CMD_START)) {
        ktota_master_start();
        printf("kt ota master start\n");
    }else if(0 == strcmp(buf, CMD_STOP)) {
        ktota_master_stop();
        printf("kt ota master stop\n");
    }else if(0 == strcmp(buf, CMD_CLOSE)) {
        ktota_master_close();
        is_run = 0;
        printf("kt ota master close\n");
    }
}


void ota_msg_recv(void) {
    uint8_t rbuf[100];
    int32_t rlen = 0;
    uint32_t idx = 0;
    rlen = simple_uart_rec(rbuf, 100);
    if(rlen > 0) {
        printf("rec %d\n", rlen);
        _msg0_parse(rbuf, (uint32_t)rlen);
    }
}

int main(int argc, char** argv) {
    json_cfg_init();
    if( ktota_master_file_check(usercfg.file_name) ) {
        return -1;
    }
    if( simple_uart_init(usercfg.uart_name, usercfg.uart_baud) ) {
        printf("open %s faild\r\n", usercfg.uart_name);
        return -2;
    }
    demo_cmd_init();
    while(is_run) {
        demo_cmd_loop();
        if( ktota_master_serve_loop(msg_send) ) {
            usleep(1*1000);
        }else {
            usleep(10*1000);
        }
        ota_msg_recv();
    }
    simple_uart_close();
    return 0;
}




